//=============================================================================
//
//   Copyright (C) 2020 All rights reserved.
//
//   Filename:		record.rs
//
//   Author: Wang Zhecheng - wangzhecheng@yeah.net
//
//   Date: 2020-10-12 14:04
//
//   Last Editors: Wang Zhecheng - wangzhecheng@yeah.net
//
//   Last modified:	2020-11-06 18:27
//
//   Description:
//
//=============================================================================
use crate::arith::tensor::{Strain, Stress};
use crate::dimension::DimType;
use crate::Real;

#[derive(Debug, Clone)]
pub enum Value<D: DimType> {
    Bool(bool),
    Int(u8),
    Scalar(Real),
    Vector(Vec<Real>),
    Stress(Stress<D>),
    Strain(Strain<D>),
}
impl<D: DimType> Value<D> {
    /// fetch as a bool
    #[inline]
    pub fn as_bool(&self) -> bool {
        match *self {
            Value::Bool(ref v) => *v,
            _ => panic!("not a bool value"),
        }
    }
    /// fetch as a mut bool
    #[inline]
    pub fn as_bool_mut(&mut self) -> &mut bool {
        match self {
            Value::Bool(ref mut v) => v,
            _ => panic!("not a bool value"),
        }
    }
    /// fetch as a integer
    #[inline]
    pub fn as_int(&self) -> u8 {
        match *self {
            Value::Int(ref v) => *v,
            _ => panic!("not a integer value"),
        }
    }
    /// fetch as a mut integer
    #[inline]
    pub fn as_int_mut(&mut self) -> &mut u8 {
        match self {
            Value::Int(ref mut v) => v,
            _ => panic!("not a integer value"),
        }
    }
    /// fetch as a real
    #[inline]
    pub fn as_real(&self) -> Real {
        match *self {
            Value::Scalar(ref v) => *v,
            _ => panic!("not a real value"),
        }
    }
    /// fetch as a mut real
    #[inline]
    pub fn as_real_mut(&mut self) -> &mut Real {
        match self {
            Value::Scalar(ref mut v) => v,
            _ => panic!("not a real value"),
        }
    }
    /// fetch as a vector
    #[inline]
    pub fn as_vector(&self) -> &Vec<Real> {
        match *self {
            Value::Vector(ref v) => v,
            _ => panic!("not a vector value"),
        }
    }
    /// fetch as a mut vector
    #[inline]
    pub fn as_vector_mut(&mut self) -> &mut Vec<Real> {
        match self {
            Value::Vector(ref mut v) => v,
            _ => panic!("not a vector value"),
        }
    }
    /// fetch as a stress
    #[inline]
    pub fn as_stress(&self) -> &Stress<D> {
        match *self {
            Value::Stress(ref v) => v,
            _ => panic!("not a stress value"),
        }
    }
    /// fetch as a mut stress
    #[inline]
    pub fn as_stress_mut(&mut self) -> &mut Stress<D> {
        match self {
            Value::Stress(ref mut v) => v,
            _ => panic!("not a stress value"),
        }
    }
    /// fetch as a strain
    #[inline]
    pub fn as_strain(&self) -> &Strain<D> {
        match *self {
            Value::Strain(ref v) => v,
            _ => panic!("not a strain value"),
        }
    }
    /// fetch as a mut strain
    #[inline]
    pub fn as_strain_mut(&mut self) -> &mut Strain<D> {
        match self {
            Value::Strain(ref mut v) => v,
            _ => panic!("not a strain value"),
        }
    }

    /// reset all data in current record
    #[inline]
    pub fn reset(&mut self) {
        match self {
            Value::Bool(v) => *v = false,
            Value::Int(v) => *v = 0,
            Value::Scalar(v) => *v = 0 as Real,
            Value::Vector(v) => v.clear(),
            Value::Stress(v) => {
                v.reset();
            }
            Value::Strain(v) => {
                v.reset();
            }
        }
    }
}

#[derive(Debug, Clone, Default)]
pub struct Record<D: DimType> {
    data: std::collections::HashMap<String, Value<D>>,
}

impl<D: DimType> std::ops::Index<&str> for Record<D> {
    type Output = Value<D>;
    #[inline]
    fn index(&self, key: &str) -> &Self::Output {
        self.data.index(&key.to_string())
    }
}

impl<D: DimType> Record<D> {
    #[inline]
    pub fn new() -> Self {
        Self {
            data: std::collections::HashMap::new(),
        }
    }
    #[inline]
    pub fn reset(&mut self) {
        for (_, v) in self.data.iter_mut() {
            v.reset();
        }
    }
    ///
    ///
    ///
    #[inline]
    pub fn get(&self, key: &str) -> &Value<D> {
        match self.data.get(&key.to_string()) {
            None => panic!("no {}", key),
            Some(v) => v,
        }
    }
}
///
/// ---------------------------------------------------------
///
pub trait Insert<D> {
    fn insert(&mut self, key: &str, v: D);
}
impl<D: DimType> Insert<bool> for Record<D> {
    #[inline]
    fn insert(&mut self, key: &str, v: bool) {
        self.data.insert(key.to_string(), Value::Bool(v));
    }
}
impl<D: DimType> Insert<u8> for Record<D> {
    #[inline]
    fn insert(&mut self, key: &str, v: u8) {
        self.data.insert(key.to_string(), Value::Int(v));
    }
}
impl<D: DimType> Insert<Real> for Record<D> {
    #[inline]
    fn insert(&mut self, key: &str, v: Real) {
        self.data.insert(key.to_string(), Value::Scalar(v));
    }
}
impl<D: DimType> Insert<Vec<Real>> for Record<D> {
    #[inline]
    fn insert(&mut self, key: &str, v: Vec<Real>) {
        self.data.insert(key.to_string(), Value::Vector(v));
    }
}
impl<D: DimType> Insert<Stress<D>> for Record<D> {
    #[inline]
    fn insert(&mut self, key: &str, v: Stress<D>) {
        self.data.insert(key.to_string(), Value::Stress(v));
    }
}
impl<D: DimType> Insert<Strain<D>> for Record<D> {
    #[inline]
    fn insert(&mut self, key: &str, v: Strain<D>) {
        self.data.insert(key.to_string(), Value::Strain(v));
    }
}

#[test]
fn set_get_field() {
    use crate::dimension::PlaneStress;
    let mut rec = Record::<PlaneStress>::new();
    rec.insert("foo", true);
    assert!(rec["foo"].as_bool());
    assert!(rec.get("foo").as_bool());
    rec.insert("bar", 32.2 as Real);
    assert_eq!(rec["bar"].as_real(), 32.2 as Real);
    assert_eq!(rec.get("bar").as_real(), 32.2 as Real);
}
